package top.longmarch.flow.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.LiteflowResponse;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.longmarch.flow.api.ClassBeanUtil;
import top.longmarch.flow.entity.LiteflowChain;
import top.longmarch.flow.exception.BusinessException;
import top.longmarch.flow.service.ILiteflowChainService;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class FlowExecutorServer {

    @Resource
    private FlowExecutor flowExecutor;
    @Resource
    private ILiteflowChainService liteflowChainService;


    @Transactional(rollbackFor = Exception.class)
    public Object run(Map<String, Object> params) {
        String chainId = MapUtil.getStr(params, "chainId");
        validateChainId(chainId);

        LiteflowChain liteflowChain = getLiteflowChain(chainId);

        List<Class<?>> contextBeanClazzArray = ClassBeanUtil.getContextBeanClazzArray(liteflowChain.getContextBeans());

        LiteflowResponse response = executeFlow(liteflowChain, params, contextBeanClazzArray);

        if (!response.isSuccess()) {
            throw new BusinessException(5000, response.getMessage());
        }

        return responseData(response, contextBeanClazzArray);
    }

    private void validateChainId(String chainId) {
        if (StrUtil.isBlank(chainId)) {
            throw new BusinessException(5000, "流程chainId不能为空");
        }
    }

    private LiteflowChain getLiteflowChain(String chainId) {
        LiteflowChain liteflowChain = liteflowChainService.getOne(new LambdaQueryWrapper<LiteflowChain>()
                .eq(LiteflowChain::getChainName, chainId));
        if (ObjectUtil.isNull(liteflowChain)) {
            throw new BusinessException(5000, "流程未配置");
        }
        if (!liteflowChain.getEnable()) {
            throw new BusinessException(5000, "流程未启用");
        }
        if (!LiteFlowChainELBuilder.validate(liteflowChain.getElData())) {
            throw new BusinessException(5000, "流程配置错误");
        }
        if (ObjectUtil.isNull(FlowBus.getChain(chainId))) {
            throw new BusinessException(5000, "流程加载失败");
        }
        return liteflowChain;
    }

    public LiteflowResponse executeFlow(LiteflowChain liteflowChain, Map<String, Object> params, List<Class<?>> contextBeanClazzArray) {
        Object beanParams = convertParams(liteflowChain.getRequestBean(), params);
        return flowExecutor.execute2Resp(liteflowChain.getChainName(), beanParams, contextBeanClazzArray.toArray(new Class[0]));
    }

    private Object convertParams(String requestBean, Map<String, Object> params) {
        if (StrUtil.isBlank(requestBean)) {
            return params;
        }
        return BeanUtil.toBean(params, ClassBeanUtil.getContextBeanClazz(requestBean));
    }

    private Object responseData(LiteflowResponse response, List<Class<?>> contextBeanClazzArray) {
        Map<String, Object> result = new HashMap<>();
        result.put("execute_info", responseExecuteData(response));
        for (Class<?> clazz : contextBeanClazzArray) {
            String simpleName = StrUtil.toUnderlineCase(clazz.getSimpleName()).toLowerCase();
            result.put(simpleName, response.getContextBean(clazz));
        }
        return result;
    }

    private Map<String, Object> responseExecuteData(LiteflowResponse response) {
        Map<String, Object> contextBean = new HashMap<>();
        contextBean.put("request_id", response.getRequestId());
        contextBean.put("chain_id", response.getSlot().getChainId());
        contextBean.put("execute_step_without_time", response.getRollbackStepStrWithoutTime());
        contextBean.put("execute_step_with_time", response.getExecuteStepStrWithTime());
        return contextBean;
    }


}
